package cn.iocoder.yudao.module.bpm.service.taskinfo;

import cn.iocoder.yudao.framework.common.enums.ISDeleteStatusEnum;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.projectinfo.BpmProjectInfoDO;
import cn.iocoder.yudao.module.bpm.enums.projectinfo.BpmProjectInfoStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.service.projectinfo.BpmProjectInfoService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.bpm.controller.admin.taskinfo.vo.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.taskinfo.BpmTaskInfoDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.bpm.dal.mysql.taskinfo.BpmTaskInfoMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;

/**
 * 流程任务信息 Service 实现类
 *
 * @author 韩佳乐
 */
@Service
@Validated
public class BpmTaskInfoServiceImpl implements BpmTaskInfoService {

    @Resource
    private BpmTaskInfoMapper taskInfoMapper;
    @Resource
    private BpmProcessInstanceService processInstanceService;
    @Resource
    private BpmProjectInfoService bpmProjectInfoService;
    @Resource
    private PermissionApi permissionApi;

    @Override
    public String createTaskInfo(BpmTaskInfoSaveReqVO createReqVO) {

        // 1 构建任务信息
        BpmTaskInfoDO taskInfo = BeanUtils.toBean(createReqVO, BpmTaskInfoDO.class);
        taskInfo.setCode(UUID.randomUUID().toString());
        Long loginUserId = getLoginUserId();
        taskInfo.setCreator(String.valueOf(loginUserId));
        taskInfo.setCreateTime(LocalDateTime.now());
        taskInfo.setDeleted(Boolean.FALSE);
        taskInfo.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus());

        String processInstanceId = null;
        if (StringUtils.isNotBlank(createReqVO.getProcessDefinitionId())){
            // 2.1 构建流程参数
            BpmProcessInstanceCreateReqVO createProcessInstanceVO = new BpmProcessInstanceCreateReqVO();
            createProcessInstanceVO.setProcessDefinitionId(createReqVO.getProcessDefinitionId());
            createProcessInstanceVO.setVariables(createReqVO.getVariables());
            createProcessInstanceVO.setFinishedTime(LocalDateTimeUtils.localDateTimeToString(createReqVO.getEndTime(),
                    LocalDateTimeUtils.YYYY_MM_DD_HH_MM_SS));
            Map<String, List<Long>> startUserSelectAssignees = createReqVO.getStartUserSelectAssignees();
            if (Objects.nonNull(startUserSelectAssignees)){
                createProcessInstanceVO.setStartUserSelectAssignees(startUserSelectAssignees);
            }
            // 2.2 发起流程任务
            processInstanceId = processInstanceService.createProcessInstance(getLoginUserId(), createProcessInstanceVO);
            //2.3 发起流程成功后，将流程实例id保存在任务信息表中
            taskInfo.setProcessInstanceId(processInstanceId);
        }
        // 3 保存任务
        taskInfoMapper.insert(taskInfo);

        return processInstanceId;
    }

    @Override
    public void updateTaskInfo(BpmTaskInfoSaveReqVO updateReqVO) {
        // 校验存在
        validateTaskInfoExists(updateReqVO.getId());
        // 更新
        BpmTaskInfoDO updateObj = BeanUtils.toBean(updateReqVO, BpmTaskInfoDO.class);
        taskInfoMapper.updateById(updateObj);
    }

    @Override
    public void updateTaskInfoStatus(Long id, int status) {
        taskInfoMapper.updateTaskInfoStatusById(id,status);
    }

    @Override
    public void deleteTaskInfo(Long id) {
        // 校验存在
        validateTaskInfoExists(id);
        // 删除
        taskInfoMapper.deleteById(id);
    }

    private void validateTaskInfoExists(Long id) {
        if (taskInfoMapper.selectById(id) == null) {
            throw exception(TASK_INFO_NOT_EXISTS);
        }
    }

    @Override
    public BpmTaskInfoDO getTaskInfo(Long id) {
        return taskInfoMapper.selectById(id);
    }

    @Override
    public PageResult<BpmTaskInfoDO> getTaskInfoPage(BpmTaskInfoPageReqVO pageReqVO) {
        return taskInfoMapper.selectPage(pageReqVO);
    }

    @Override
    public BpmTaskInfoDO getTaskInfoByProcInstId(String id) {
        return taskInfoMapper.selectOne("process_instance_id",id);
    }

    @Override
    public BpmTaskInfoStatisticsVO getTaskInfoStatistic() {
        Long loginUserId = getLoginUserId();
        boolean isAdminRole = permissionApi.hasSuperAdminRoles(loginUserId);
        List<BpmTaskInfoDO> tasks = null;
        if (isAdminRole){
            tasks = taskInfoMapper.selectList();
        } else {
            tasks = taskInfoMapper.selectList("creator",loginUserId);
        }
        BpmTaskInfoStatisticsVO result = new BpmTaskInfoStatisticsVO();
        if (!CollectionUtils.isEmpty(tasks)){
            result.setTaskTotal(tasks.size());

            long approveCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.APPROVE.getStatus()) || Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus())).count();
            result.setFinishedTaskNum(approveCount);
            result.setFinishedStatus(BpmProcessInstanceStatusEnum.APPROVE.getStatus());
            long approvingCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.RUNNING.getStatus())).count();
            result.setRunningTaskNum(approvingCount);
            result.setRunningStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus());
            long overdueCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE.getStatus())).count();
            result.setOverdueTaskNum(overdueCount);
            result.setOverdueStatus(BpmProcessInstanceStatusEnum.OVERDUE.getStatus());
        }
        //项目信息
        List<BpmProjectInfoDO> projects = bpmProjectInfoService.getProjectInfos();
        if (!CollectionUtils.isEmpty(projects)){
            result.setProjectTotal(projects.size());
            //进行中和未开始
            long notStartedProjectCount = projects.stream().filter(obj -> Objects.equals(BpmProjectInfoStatusEnum.NOT_STARTED.getStatus(),
                    obj.getStatus())).count();
            long runningProjectCount = projects.stream().filter(obj -> Objects.equals(BpmProjectInfoStatusEnum.RUNNING.getStatus(),
                    obj.getStatus())).count();
            result.setRunningProjectNum(notStartedProjectCount + runningProjectCount);
            result.setRunningProjectStatus(BpmProjectInfoStatusEnum.RUNNING.getStatus());
            //已完成
            long finishedProjectCount = projects.stream().filter(obj -> Objects.equals(BpmProjectInfoStatusEnum.FINISHED.getStatus(),
                    obj.getStatus())).count();
            result.setFinishedProjectNum(finishedProjectCount);
            result.setFinishedProjectStatus(BpmProjectInfoStatusEnum.FINISHED.getStatus());
            //已逾期
            long overdueProjectCount = projects.stream().filter(obj -> Objects.equals(BpmProjectInfoStatusEnum.OVERDUE.getStatus(),
                    obj.getStatus())).count();
            result.setOverdueProjectNum(overdueProjectCount);
            result.setOverdueProjectStatus(BpmProjectInfoStatusEnum.OVERDUE.getStatus());
        }

        return result;
    }

    @Override
    public BpmCurrUserTaskInfoStatisticsVO getCurrUserTaskInfoStatistics() {
        //任务信息
        Long loginUserId = getLoginUserId();
        boolean isAdminRole = permissionApi.hasSuperAdminRoles(loginUserId);
        List<BpmTaskInfoDO> tasks = null;
        if (isAdminRole){
            tasks = taskInfoMapper.selectList();
        } else {
            tasks = taskInfoMapper.selectList("creator",loginUserId);
        }
        BpmCurrUserTaskInfoStatisticsVO result = new BpmCurrUserTaskInfoStatisticsVO();
        if (!CollectionUtils.isEmpty(tasks)){
            result.setTaskTotal(tasks.size());

            long approveCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.APPROVE.getStatus())).count();
            result.setFinishedTaskNum(approveCount);
            long approvingCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.RUNNING.getStatus())).count();
            result.setRunningTaskNum(approvingCount);
            long overdueCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE.getStatus())).count();
            result.setOverdueTaskNum(overdueCount);
            long overdueFinishedCount = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus())).count();
            result.setOverdueFinishedTaskNum(overdueFinishedCount);
        }
        return result;
    }

    @Override
    public BpmTaskInfoCheckStatusVO checkTaskStatusInfo(Long projectId,Long userId) {
        if (Objects.isNull(projectId)){
            throw exception(TASK_STATUS_CHECK_PROJECT_ID_EXISTS);
        }
        BpmTaskInfoCheckStatusVO result = new BpmTaskInfoCheckStatusVO();
        QueryWrapper<BpmTaskInfoDO> wrapper = new QueryWrapper<>();
        wrapper.eq("project_id", projectId);
//        wrapper.eq("creator", userId);
        List<BpmTaskInfoDO> tasks = taskInfoMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(tasks)){
            result.setIsRunning(Boolean.TRUE);
        } else {
            long overdueNum = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE.getStatus())).count();
            long finishedNum = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.APPROVE.getStatus())).count();
            long finishedOverdueNum = tasks.stream().filter(obj -> Objects.equals(obj.getStatus(),
                    BpmProcessInstanceStatusEnum.OVERDUE_FINISHED.getStatus())).count();
            if (overdueNum != 0){
                //有逾期任务，项目逾期
                result.setIsOverdue(Boolean.TRUE);
            }else if ((finishedNum + finishedOverdueNum) == tasks.size()){
                //项目全部为审批通过或逾期已完成，则项目为完成状态
                result.setIsFinished(Boolean.TRUE);
            } else {
                result.setIsRunning(Boolean.TRUE);
            }
        }
        return result;
    }

    @Override
    public List<BpmProjectTaskManHourStatisticsVO> getProjectManHourStatistics() {
        List<BpmProjectTaskManHourStatisticsVO> result = new ArrayList<>();
        List<BpmProjectInfoDO> projects = bpmProjectInfoService.getProjectInfos();
        Map<Long, BpmProjectInfoDO> projectMap = projects.stream().collect(Collectors.toMap(BpmProjectInfoDO::getId, Function.identity()));
        Long loginUserId = getLoginUserId();
        boolean isAdminRole = permissionApi.hasSuperAdminRoles(loginUserId);
        List<BpmTaskInfoDO> tasks = null;
        if (isAdminRole){
            tasks = taskInfoMapper.selectList();
        } else {
            tasks = taskInfoMapper.selectList("creator",loginUserId);
        }
        if (!CollectionUtils.isEmpty(tasks)){
            Map<Long, List<BpmTaskInfoDO>> taskMaps = tasks.stream().collect(Collectors.groupingBy(BpmTaskInfoDO::getProjectId));
            for(Long key : taskMaps.keySet()){
                List<BpmTaskInfoDO> list = taskMaps.get(key);
                if (CollectionUtils.isEmpty(list)){
                    continue;
                }
                Set<String> procInstId = list.stream().map(BpmTaskInfoDO::getProcessInstanceId).collect(Collectors.toSet());
                BpmProcessInstancePageReqVO param = new BpmProcessInstancePageReqVO();
                param.setStatus(BpmProcessInstanceStatusEnum.APPROVE.getStatus());
                //获取审批完成的流程实例
                List<HistoricProcessInstance> instances = processInstanceService.getAllProcessInstance(null,param, procInstId);
                Map<String, HistoricProcessInstance> instanceMap = null;
                if (!CollectionUtils.isEmpty(instances)){
                    instanceMap = instances.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, Function.identity(),
                            (k1, k2) -> k2));
                }
                BpmProjectTaskManHourStatisticsVO statisticsVO = new BpmProjectTaskManHourStatisticsVO();
                //1.任务预计耗时
                long duration = 0;
                //2.任务时间耗时
                long actualDuration = 0;
                for (BpmTaskInfoDO info : list){
                    duration += LocalDateTimeUtils.calculateTimeDifferenceInMillis(info.getStartTime(), info.getEndTime());
                    if (Objects.nonNull(instanceMap)){
                        HistoricProcessInstance instance = instanceMap.get(info.getProcessInstanceId());
                        actualDuration += Objects.nonNull(instance) ? instance.getDurationInMillis() : 0;
                    }
                }
                statisticsVO.setProjectManHour(duration);
                statisticsVO.setProjectActualManHour(actualDuration);
                //3.设置项目的名称
                BpmProjectInfoDO projectInfoDO = projectMap.get(key);
                statisticsVO.setProjectId(key);
                statisticsVO.setProjectName(projectInfoDO.getName());
                result.add(statisticsVO);
            }
        }
        return result;
    }
}